<!DOCTYPE html>
<html class="writer-html5" lang="zh" >
<head>
  <meta charset="utf-8" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />

  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Ch5-2 2D贴图及生成Mipmap &mdash; EasyVulkan</title>
      <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
      <link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
      <link rel="stylesheet" href="_static/theme.css" type="text/css" />
  <!--[if lt IE 9]>
    <script src="_static/js/html5shiv.min.js"></script>
  <![endif]-->
  
        <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
        <script src="_static/jquery.js"></script>
        <script src="_static/underscore.js"></script>
        <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
        <script src="_static/doctools.js"></script>
    <script src="_static/js/theme.js"></script>
    <link rel="index" title="索引" href="genindex.html" />
    <link rel="search" title="搜索" href="search.html" />
    <link rel="next" title="Ch7-1 初识顶点缓冲区" href="Ch7-1%20%E5%88%9D%E8%AF%86%E9%A1%B6%E7%82%B9%E7%BC%93%E5%86%B2%E5%8C%BA.html" />
    <link rel="prev" title="Ch5-1 各种缓冲区" href="Ch5-1%20%E5%90%84%E7%A7%8D%E7%BC%93%E5%86%B2%E5%8C%BA.html" /> 
</head>

<body class="wy-body-for-nav"> 
  <div class="wy-grid-for-nav">
    <nav data-toggle="wy-nav-shift" class="wy-nav-side">
      <div class="wy-side-scroll">
        <div class="wy-side-nav-search" >
            <a href="index.html" class="icon icon-home"> EasyVulkan
            <img src="_static/logo1.png" class="logo" alt="Logo"/>
          </a>
<div role="search">
  <form id="rtd-search-form" class="wy-form" action="search.html" method="get">
    <input type="text" name="q" placeholder="在文档中搜索" />
    <input type="hidden" name="check_keywords" value="yes" />
    <input type="hidden" name="area" value="default" />
  </form>
</div>
        </div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
              <p class="caption" role="heading"><span class="caption-text">第一章 初始化</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch1-0%20%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C.html">Ch1-0 准备工作</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch1-1%20%E5%88%9B%E5%BB%BAGLFW%E7%AA%97%E5%8F%A3.html">Ch1-1 创建GLFW窗口</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch1-2%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%B5%81%E7%A8%8B.html">Ch1-2 初始化流程</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch1-3%20%E5%88%9B%E5%BB%BAVK%E5%AE%9E%E4%BE%8B%E4%B8%8E%E9%80%BB%E8%BE%91%E8%AE%BE%E5%A4%87.html">Ch1-3 创建VK实例与逻辑设备</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch1-4%20%E5%88%9B%E5%BB%BA%E4%BA%A4%E6%8D%A2%E9%93%BE.html">Ch1-4 创建交换链</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第二章 绘制一个三角形</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch2-0%20%E4%BB%A3%E7%A0%81%E6%95%B4%E7%90%86%E5%8F%8A%E4%B8%80%E4%BA%9B%E8%BE%85%E5%8A%A9%E7%B1%BB.html">Ch2-0 代码整理及一些辅助类</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch2-1%20Rendering%20Loop.html">Ch2-1 Rendering Loop</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch2-2%20%E5%88%9B%E5%BB%BA%E6%B8%B2%E6%9F%93%E9%80%9A%E9%81%93%E5%92%8C%E5%B8%A7%E7%BC%93%E5%86%B2.html">Ch2-2 创建渲染通道和帧缓冲</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch2-3%20%E5%88%9B%E5%BB%BA%E7%AE%A1%E7%BA%BF%E5%B9%B6%E7%BB%98%E5%88%B6%E4%B8%89%E8%A7%92%E5%BD%A2.html">Ch2-3 创建管线并绘制三角形</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第三章 纵观Vulkan</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch3-1%20%E5%90%8C%E6%AD%A5%E5%8E%9F%E8%AF%AD.html">Ch3-1 同步原语</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-2%20%E5%9B%BE%E5%83%8F%E4%B8%8E%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch3-2 图像与缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-3%20%E7%AE%A1%E7%BA%BF%E5%B8%83%E5%B1%80%E5%92%8C%E7%AE%A1%E7%BA%BF.html">Ch3-3 管线布局和管线</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-4%20%E6%B8%B2%E6%9F%93%E9%80%9A%E9%81%93%E5%92%8C%E5%B8%A7%E7%BC%93%E5%86%B2.html">Ch3-4 渲染通道和帧缓冲</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-5%20%E5%91%BD%E4%BB%A4%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch3-5 命令缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-6%20%E6%8F%8F%E8%BF%B0%E7%AC%A6.html">Ch3-6 描述符</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-7%20%E9%87%87%E6%A0%B7%E5%99%A8.html">Ch3-7 采样器</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第四章 着色器</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch4-1%20%E7%9D%80%E8%89%B2%E5%99%A8%E6%A8%A1%E7%BB%84.html">Ch4-1 着色器模组</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch4-2%20%E9%A1%B6%E7%82%B9%E7%9D%80%E8%89%B2%E5%99%A8.html">Ch4-2 顶点着色器</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch4-3%20%E7%89%87%E6%AE%B5%E7%9D%80%E8%89%B2%E5%99%A8.html">Ch4-3 片段着色器</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第五章 封装常用对象</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="Ch5-0%20VkBase%2B.h.html">Ch5-0 VkBase+.h</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch5-1%20%E5%90%84%E7%A7%8D%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch5-1 各种缓冲区</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Ch5-2 2D贴图及生成Mipmap</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#id1">贴图基类</a></li>
<li class="toctree-l2"><a class="reference internal" href="#d">2D贴图</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#blitandgeneratemipmap2d">BlitAndGenerateMipmap2d</a></li>
<li class="toctree-l3"><a class="reference internal" href="#copyblitandgeneratemipmap2d">CopyBlitAndGenerateMipmap2d</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="#mipmap">生成Mipmap</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#mip">计算Mip等级数</a></li>
<li class="toctree-l3"><a class="reference internal" href="#cmdgeneratemipmap2d">CmdGenerateMipmap2d</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第七章 基础示例</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch7-1%20%E5%88%9D%E8%AF%86%E9%A1%B6%E7%82%B9%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch7-1 初识顶点缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-2%20%E5%88%9D%E8%AF%86%E7%B4%A2%E5%BC%95%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch7-2 初识索引缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-3%20%E5%88%9D%E8%AF%86Push%20Constant.html">Ch7-3 初识Push Constant</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-4%20%E5%88%9D%E8%AF%86Uniform%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch7-4 初识Uniform缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-5%20%E6%8B%B7%E8%B4%9D%E5%9B%BE%E5%83%8F%E5%88%B0%E5%B1%8F%E5%B9%95.html">Ch7-5 拷贝图像到屏幕</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-6%20%E4%BD%BF%E7%94%A8%E8%B4%B4%E5%9B%BE.html">Ch7-6 使用贴图</a></li>
</ul>

        </div>
      </div>
    </nav>

    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
          <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
          <a href="index.html">EasyVulkan</a>
      </nav>

      <div class="wy-nav-content">
        <div class="rst-content">
          <div role="navigation" aria-label="Page navigation">
  <ul class="wy-breadcrumbs">
      <li><a href="index.html" class="icon icon-home"></a> &raquo;</li>
      <li>Ch5-2 2D贴图及生成Mipmap</li>
      <li class="wy-breadcrumbs-aside">
      </li>
  </ul>
  <hr/>
</div>
          <div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
           <div itemprop="articleBody">
             
  <section id="ch5-2-2dmipmap">
<h1>Ch5-2 2D贴图及生成Mipmap<a class="headerlink" href="#ch5-2-2dmipmap" title="Permalink to this heading"></a></h1>
<p>
    请搭配<a class="reference internal" href="Ch7-6%20%E4%BD%BF%E7%94%A8%E8%B4%B4%E5%9B%BE.html">Ch7-6 使用贴图</a>食用本节。
</p><section id="id1">
<h2>贴图基类<a class="headerlink" href="#id1" title="Permalink to this heading"></a></h2>
<p>
    先前已在<a class="reference internal" href="Ch7-5%20%E6%8B%B7%E8%B4%9D%E5%9B%BE%E5%83%8F%E5%88%B0%E5%B1%8F%E5%B9%95.html#id2">读取图像数据</a>中创建了<span class="type">texture</span>类，这个类将会是之后一系列贴图类的基类。
    <br>
    之后每个贴图类，都会需要一个<span class="type">imageView</span>对象和<span class="type">imageMemory</span>对象，先定义相应的成员变量、Getter和创建函数：
</p>
<pre class="code">
<span class="kw">class</span> <span class="type">texture</span> {
<span class="kw">protected</span>:
    <span class="type">imageView</span> imageView;
    <span class="type">imageMemory</span> imageMemory;
    <span class="cmt">//--------------------</span>
    <span class="fn">texture</span>() = <span class="kw">default</span>;
    <span class="cmt">//该函数用于方便地创建imageMemory</span>
    <span class="kw">void</span> <span class="fn">CreateImageMemory</span>(<span class="type">VkImageType</span> imageType, <span class="type">VkFormat</span> format, <span class="type">VkExtent3D</span> extent, <span class="type">uint32_t</span> mipLevelCount, <span class="type">uint32_t</span> arrayLayerCount, <span class="type">VkImageCreateFlags</span> flags = 0) {
        <span class="type">VkImageCreateInfo</span> imageCreateInfo = {
            .flags = flags,
            .imageType = imageType,
            .format = format,
            .extent = extent,
            .mipLevels = mipLevelCount,
            .arrayLayers = arrayLayerCount,
            .samples = <span class="enum">VK_SAMPLE_COUNT_1_BIT</span>,
            .usage = <span class="enum">VK_IMAGE_USAGE_TRANSFER_SRC_BIT</span> | <span class="enum">VK_IMAGE_USAGE_TRANSFER_DST_BIT</span> | <span class="enum">VK_IMAGE_USAGE_SAMPLED_BIT</span>
        };
        imageMemory.<span class="fn">Create</span>(imageCreateInfo, <span class="enum">VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT</span>);
    }
    <span class="cmt">//该函数用于方便地创建imageView</span>
    <span class="kw">void</span> <span class="fn">CreateImageView</span>(<span class="type">VkImageViewType</span> viewType, <span class="type">VkFormat</span> format, <span class="type">uint32_t</span> mipLevelCount, <span class="type">uint32_t</span> arrayLayerCount, <span class="type">VkImageViewCreateFlags</span> flags = 0) {
        imageView.<span class="fn">Create</span>(imageMemory.<span class="fn">Image</span>(), viewType, format, { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, mipLevelCount, 0, arrayLayerCount }, flags);
    }
    <span class="cmt">//Static Function</span>
    <span class="kw">static</span> std::<span class="type">unique_ptr</span>&lt;<span class="type">uint8_t</span>[]&gt; <span class="fn">LoadFile_Internal</span>(<span class="kw">const auto</span>* address, <span class="type">size_t</span> fileSize, <span class="type">VkExtent2D</span>&amp; extent, <span class="type">formatInfo</span> requiredFormatInfo) {
        <span class="cmt">/*...*/</span>
    }
<span class="kw">public</span>:
    <span class="cmt">//Getter</span>
    <span class="type">VkImageView</span> <span class="fn">ImageView</span>() <span class="kw">const</span> { <span class="kw">return</span> imageView; }
    <span class="type">VkImage</span> <span class="fn">Image</span>() <span class="kw">const</span> { <span class="kw">return</span> imageMemory.<span class="fn">Image</span>(); }
    <span class="kw">const</span> <span class="type">VkImageView</span>* <span class="fn">AddressOfImageView</span>() <span class="kw">const</span> { <span class="kw">return</span> imageView.<span class="fn">Address</span>(); }
    <span class="kw">const</span> <span class="type">VkImage</span>* <span class="fn">AddressOfImage</span>() <span class="kw">const</span> { <span class="kw">return</span> imageMemory.<span class="fn">AddressOfImage</span>(); }
    <span class="cmt">//Const Function</span>
    <span class="type">VkDescriptorImageInfo</span> <span class="fn">DescriptorImageInfo</span>(<span class="type">VkSampler</span> sampler) <span class="kw">const</span> {
        <span class="kw">return</span> { sampler, imageView, <span class="enum">VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL</span> };
    }
    <span class="cmt">//Static Function</span>
    [[nodiscard]]
    <span class="kw">static</span> std::<span class="type">unique_ptr</span>&lt;<span class="type">uint8_t</span>[]&gt; <span class="fn">LoadFile</span>(<span class="kw">const char</span>* filepath, <span class="type">VkExtent2D</span>&amp; extent, <span class="type">formatInfo</span> requiredFormatInfo) {
        <span class="kw">return</span> <span class="fn">LoadFile_Internal</span>(filepath, 0, extent, requiredFormatInfo);
    }
    [[nodiscard]]
    <span class="kw">static</span> std::<span class="type">unique_ptr</span>&lt;<span class="type">uint8_t</span>[]&gt; <span class="fn">LoadFile</span>(<span class="kw">const</span> <span class="type">uint8_t</span>* fileBinaries, <span class="type">size_t</span> fileSize, <span class="type">VkExtent2D</span>&amp; extent, <span class="type">formatInfo</span> requiredFormatInfo) {
        <span class="kw">return</span> <span class="fn">LoadFile_Internal</span>(fileBinaries, fileSize, extent, requiredFormatInfo);
    }
    <span class="kw">static</span> <span class="type">uint32_t</span> <span class="fn">CalculateMipLevelCount</span>(<span class="type">VkExtent2D</span> extent) {
        <span class="cmt">/*涉及生成mipmap，待填充*/</span>
    }
    <span class="kw">static void</span> <span class="fn">CopyBlitAndGenerateMipmap2d</span>(<span class="type">VkBuffer</span> buffer_copyFrom, <span class="type">VkImage</span> image_copyTo, <span class="type">VkImage</span> image_blitTo, <span class="type">VkExtent2D</span> imageExtent,
        <span class="type">uint32_t</span> mipLevelCount = 1, <span class="type">uint32_t</span> layerCount = 1, <span class="type">VkFilter</span> minFilter = <span class="enum">VK_FILTER_LINEAR</span>) {
        <span class="cmt">/*涉及生成mipmap，待填充*/</span>
    }
    <span class="kw">static void</span> <span class="fn">BlitAndGenerateMipmap2d</span>(<span class="type">VkImage</span> image_preinitialized, <span class="type">VkImage</span> image_final, <span class="type">VkExtent2D</span> imageExtent,
        <span class="type">uint32_t</span> mipLevelCount = 1, <span class="type">uint32_t</span> layerCount = 1, <span class="type">VkFilter</span> minFilter = <span class="enum">VK_FILTER_LINEAR</span>) {
        <span class="cmt">/*涉及生成mipmap，待填充*/</span>
    }
};
</pre>
<ul>
    <li>
        <p>
            既然是被用来采样的图像，图像用途当然得包含<span class="enum">VK_IMAGE_USAGE_SAMPLED_BIT</span>，而包含<span class="enum">VK_IMAGE_USAGE_TRANSFER_SRC_BIT</span>是因为之后生成mipmap时要将其作为源图像。
        </p>
    </li>
    <li>
        <p>
            贴图的采样点个数当然是<span class="enum">VK_SAMPLE_COUNT_1_BIT</span>，多重采样图像不能被用作各种传输命令的源和目标，而且指定事先准备好的贴图为多重采样也没有意义（注：GLSL中的<span class="type">texture2DMS</span>等类型，用于需要在着色器中读取多重采样的图像附件的情况，与一般的贴图无关）。
        </p>
    </li>
    <li>
        <p>
            <span class="fn">DescriptorImageInfo</span>(...)返回写入描述符时所需的信息，因为图像被用于在着色器中采样，内存布局当然是<span class="enum">VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL</span>。
        </p>
    </li>
</ul></section>
<section id="d">
<h2>2D贴图<a class="headerlink" href="#d" title="Permalink to this heading"></a></h2>
<p>
    从<span class="type">texture</span>派生出<span class="type">texture2d</span>，新增的成员变量只有记录图像大小的extent：
</p>
<pre class="code">
<span class="kw">class</span> <span class="type">texture2d</span> :<span class="kw">public</span> <span class="type">texture</span> {
<span class="kw">protected</span>:
    <span class="type">VkExtent2D</span> extent = {};
    <span class="cmt">//--------------------</span>
    <span class="kw">void</span> <span class="fn">Create_Internal</span>(<span class="type">VkFormat</span> format_initial, <span class="type">VkFormat</span> format_final, <span class="kw">bool</span> generateMipmap) {
        <span class="cmt">/*待填充*/</span>
    }
<span class="kw">public</span>:
    <span class="fn">texture2d</span>() = <span class="kw">default</span>;
    <span class="fn">texture2d</span>(<span class="kw">const char</span>* filepath, <span class="type">VkFormat</span> format_initial, <span class="type">VkFormat</span> format_final, <span class="kw">bool</span> generateMipmap = <span class="kw">true</span>) {
        <span class="fn">Create</span>(filepath, format_initial, format_final, generateMipmap);
    }
    <span class="fn">texture2d</span>(<span class="kw">const</span> <span class="type">uint8_t</span>* pImageData, <span class="type">VkExtent2D</span> extent, <span class="type">VkFormat</span> format_initial, <span class="type">VkFormat</span> format_final, <span class="kw">bool</span> generateMipmap = <span class="kw">true</span>) {
        <span class="fn">Create</span>(pImageData, extent, format_initial, format_final, generateMipmap);
    }
    <span class="cmt">//Getter</span>
    <span class="type">VkExtent2D</span> <span class="fn">Extent</span>() <span class="kw">const</span> { <span class="kw">return</span> extent; }
    <span class="type">uint32_t</span> <span class="fn">Width</span>() <span class="kw">const</span> { <span class="kw">return</span> extent.width; }
    <span class="type">uint32_t</span> <span class="fn">Height</span>() <span class="kw">const</span> { <span class="kw">return</span> extent.height; }
    <span class="cmt">//Non-const Function</span>
    <span class="kw">void</span> <span class="fn">Create</span>(<span class="kw">const char</span>* filepath, <span class="type">VkFormat</span> format_initial, <span class="type">VkFormat</span> format_final, <span class="kw">bool</span> generateMipmap = <span class="kw">true</span>) {
        <span class="type">VkExtent2D</span> extent;
        <span class="type">formatInfo</span> formatInfo = <span class="fn">FormatInfo</span>(format_initial);<span class="cmt">//根据指定的format_initial取得格式信息</span>
        std::<span class="type">unique_ptr</span>&lt;<span class="type">uint8_t</span>[]&gt; pImageData = <span class="fn">LoadFile</span>(filepath, extent, formatInfo);
        <span class="kw">if</span> (pImageData)
            <span class="fn">Create</span>(pImageData.<span class="fn">get</span>(), extent, format_initial, format_final, generateMipmap);
    }
    <span class="kw">void</span> <span class="fn">Create</span>(<span class="kw">const</span> <span class="type">uint8_t</span>* pImageData, <span class="type">VkExtent2D</span> extent, <span class="type">VkFormat</span> format_initial, <span class="type">VkFormat</span> format_final, <span class="kw">bool</span> generateMipmap = <span class="kw">true</span>) {
        <span class="kw">this</span>->extent = extent;
        <span class="type">size_t</span> imageDataSize = <span class="type">size_t</span>(<span class="fn">FormatInfo</span>(format_initial).sizePerPixel) * extent.width * extent.height;
        <span class="type">stagingBuffer</span>::<span class="fn">BufferData_MainThread</span>(pImageData, imageDataSize);<span class="cmt">//拷贝数据到暂存缓冲区</span>
        <span class="fn">Create_Internal</span>(format_initial, format_final, generateMipmap);
    }
};
</pre>
<p>
    注意到参数<span class="var">format_initial</span>和<span class="var">format_final</span>，读取到的图像数据的格式，可能与我们最终期望的图像格式不同，比如stb_image读取hdr文件得到的是32位浮点数，而通常16位的精度便已足够，出于节省设备内存的目的，往往会使用blit命令进行图像格式转换。
    <br>
    填充<span class="fn">Create_Internal</span>(...)，首先根据<span class="var">generateMipmap</span>的值，确定是否调用<span class="fn">CalculateMipLevelCount</span>(...)计算mipmap的等级总数：
</p>
<pre class="code">
<span class="type">uint32_t</span> mipLevelCount = generateMipmap ? <span class="fn">CalculateMipLevelCount</span>(extent) : 1;
</pre>
<p>
    <span class="fn">CalculateMipLevelCount</span>(...)的函数体留到下一小节填充，先接着往下写，创建imageMemory和imageView：
</p>
<pre class="code">
<span class="type">uint32_t</span> mipLevelCount = generateMipmap ? <span class="fn">CalculateMipLevelCount</span>(extent) : 1;
<span class="cmt">//创建图像并分配内存</span>
<span class="fn">CreateImageMemory</span>(<span class="enum">VK_IMAGE_TYPE_2D</span>, format_final, { extent.width, extent.height, 1 }, mipLevelCount, 1);
<span class="cmt">//创建图像视图</span>
<span class="fn">CreateImageView</span>(<span class="enum">VK_IMAGE_VIEW_TYPE_2D</span>, format_final, mipLevelCount, 1);
</pre>
<p>
    根据是否需要格式转换来进行分支：
</p>
<pre class="code">
<span class="kw">if</span> (format_initial == format_final)
    <span class="cmt">/*待填充*/</span>
<span class="kw">else</span>
    <span class="cmt">/*待填充*/</span>
</pre>
<p>
    接下来需要使用先前声明的两个函数：
</p>
<ul>
    <li>
        <p>
            <span class="fn">CopyBlitAndGenerateMipmap2d</span>(<span class="type">VkBuffer</span> buffer_copyFrom, <span class="type">VkImage</span> image_copyTo, <span class="type">VkImage</span> image_blitTo, <span class="cmt">/*...*/</span>)
            <br>
            将数据从<span class="var">buffer_copyFrom</span>拷贝到<span class="var">image_copyTo</span>，然后在从<span class="var">image_copyTo</span>将数据blit到<span class="var">image_blitTo</span>，然后生成mipmap。
        </p>
    </li>
    <li>
        <p>
            <span class="fn">BlitAndGenerateMipmap2d</span>(<span class="type">VkImage</span> image_preinitialized, <span class="type">VkImage</span> image_final, <span class="cmt">/*...*/</span>)
            <br>
            从<span class="var">image_preinitialized</span>将数据blit到<span class="var">image_final</span>，其中<span class="var">image_preinitialized</span>指的是暂存缓冲区的混叠图像，然后生成mipmap。
        </p>
    </li>
</ul>
<p>
    两个函数中还会处理如下逻辑：若blit的源图像和目标图像一致，则不会进行blit。
    <br>
    希望这不会让你觉得过度封装。<span class="fn">CopyBlitAndGenerateMipmap2d</span>(...)还会在之后创建2D贴图数组和立方体贴图时派上用场，如果不做这种封装，代码会写得重复而麻烦。函数体留到之后填充，先完成这块的代码逻辑。
</p>
<p>
    你可以回忆下<a class="reference internal" href="Ch7-5%20%E6%8B%B7%E8%B4%9D%E5%9B%BE%E5%83%8F%E5%88%B0%E5%B1%8F%E5%B9%95.html#id1">Ch7-5</a>，然后就会明白为何会有上述两个函数，以及该如何区分使用它们：重点在于能否为暂存缓冲区创建混叠图像。
</p>
<pre class="code">
<span class="kw">if</span> (format_initial == format_final)
    <span class="cmt">//若不需要格式转换，直接从暂存缓冲区拷贝到图像，不发生blit</span>
    <span class="fn">CopyBlitAndGenerateMipmap2d</span>(<span class="type">stagingBuffer</span>::<span class="fn">Buffer_MainThread</span>(), imageMemory.<span class="fn">Image</span>(), imageMemory.<span class="fn">Image</span>(), extent, mipLevelCount, 1);
<span class="kw">else</span>
    <span class="kw">if</span> (<span class="type">VkImage</span> image_conversion = <span class="type">stagingBuffer</span>::<span class="fn">AliasedImage2d_MainThread</span>(format_initial, extent))
        <span class="cmt">//若需要格式转换，但是能为暂存缓冲区创建混叠图像，则直接blit</span>
        <span class="fn">BlitAndGenerateMipmap2d</span>(image_conversion, imageMemory.<span class="fn">Image</span>(), extent, mipLevelCount, 1);
    <span class="kw">else</span> {
        <span class="cmt">//否则，创建新的暂存图像用于中转</span>
        <span class="type">VkImageCreateInfo</span> imageCreateInfo = {
            .imageType = <span class="enum">VK_IMAGE_TYPE_2D</span>,
            .format = format_initial,
            .extent = { extent.width, extent.height, 1 },
            .mipLevels = 1,
            .arrayLayers = 1,
            .samples = <span class="enum">VK_SAMPLE_COUNT_1_BIT</span>,
            .usage = <span class="enum">VK_IMAGE_USAGE_TRANSFER_SRC_BIT</span> | <span class="enum">VK_IMAGE_USAGE_TRANSFER_DST_BIT</span>
        };
        vulkan::<span class="type">imageMemory</span> imageMemory_conversion(imageCreateInfo, <span class="enum">VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT</span>);
        <span class="cmt">//从暂存缓冲区拷贝到图像，然后再blit</span>
        <span class="fn">CopyBlitAndGenerateMipmap2d</span>(<span class="type">stagingBuffer</span>::<span class="fn">Buffer_MainThread</span>(), imageMemory_conversion.<span class="fn">Image</span>(), imageMemory.<span class="fn">Image</span>(), extent, mipLevelCount, 1);
    }
</pre>
<p>
    相信上述逻辑不需要多做说明，整个<span class="fn">Create_Internal</span>(...)如下：
</p>
<pre class="code">
<span class="kw">void</span> <span class="fn">Create_Internal</span>(<span class="type">VkFormat</span> format_initial, <span class="type">VkFormat</span> format_final, <span class="kw">bool</span> generateMipmap) {
    <span class="type">uint32_t</span> mipLevelCount = generateMipmap ? <span class="fn">CalculateMipLevelCount</span>(extent) : 1;
    <span class="cmt">//创建图像并分配内存</span>
    <span class="fn">CreateImageMemory</span>(<span class="enum">VK_IMAGE_TYPE_2D</span>, format_final, { extent.width, extent.height, 1 }, mipLevelCount, 1);
    <span class="cmt">//创建图像视图</span>
    <span class="fn">CreateImageView</span>(<span class="enum">VK_IMAGE_VIEW_TYPE_2D</span>, format_final, mipLevelCount, 1);
    <span class="cmt">//Blit数据到图像，并生成mipmap</span>
    <span class="kw">if</span> (format_initial == format_final)
        <span class="fn">CopyBlitAndGenerateMipmap2d</span>(<span class="type">stagingBuffer</span>::<span class="fn">Buffer_MainThread</span>(), imageMemory.<span class="fn">Image</span>(), imageMemory.<span class="fn">Image</span>(), extent, mipLevelCount, 1);
    <span class="kw">else</span>
        <span class="kw">if</span> (<span class="type">VkImage</span> image_conversion = <span class="type">stagingBuffer</span>::<span class="fn">AliasedImage2d_MainThread</span>(format_initial, extent))
            <span class="fn">BlitAndGenerateMipmap2d</span>(image_conversion, imageMemory.<span class="fn">Image</span>(), extent, mipLevelCount, 1);
        <span class="kw">else</span> {
            <span class="type">VkImageCreateInfo</span> imageCreateInfo = {
                .imageType = <span class="enum">VK_IMAGE_TYPE_2D</span>,
                .format = format_initial,
                .extent = { extent.width, extent.height, 1 },
                .mipLevels = 1,
                .arrayLayers = 1,
                .samples = <span class="enum">VK_SAMPLE_COUNT_1_BIT</span>,
                .usage = <span class="enum">VK_IMAGE_USAGE_TRANSFER_SRC_BIT</span> | <span class="enum">VK_IMAGE_USAGE_TRANSFER_DST_BIT</span>
            };
            vulkan::<span class="type">imageMemory</span> imageMemory_conversion(imageCreateInfo, <span class="enum">VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT</span>);
            <span class="fn">CopyBlitAndGenerateMipmap2d</span>(<span class="type">stagingBuffer</span>::<span class="fn">Buffer_MainThread</span>(), imageMemory_conversion.<span class="fn">Image</span>(), imageMemory.<span class="fn">Image</span>(), extent, mipLevelCount, 1);
        }
}
</pre><section id="blitandgeneratemipmap2d">
<h3>BlitAndGenerateMipmap2d<a class="headerlink" href="#blitandgeneratemipmap2d" title="Permalink to this heading"></a></h3>
<p>
    填充<span class="fn">BlitAndGenerateMipmap2d</span>(...)，首先来判定是否有必要blit或生成mipmap，如皆无必要则函数直接执行完毕：
</p>
<pre class="code">
<span class="kw">static void</span> <span class="fn">BlitAndGenerateMipmap2d</span>(<span class="type">VkImage</span> image_preinitialized, <span class="type">VkImage</span> image_final, <span class="type">VkExtent2D</span> imageExtent,
    <span class="type">uint32_t</span> mipLevelCount = 1, <span class="type">uint32_t</span> layerCount = 1, <span class="type">VkFilter</span> minFilter = <span class="enum">VK_FILTER_LINEAR</span>) {
    <span class="cmt">//生成mipmap的条件是mip等级大于1</span>
    <span class="kw">bool</span> generateMipmap = mipLevelCount > 1;
    <span class="cmt">//发生成blit的条件是源图像和目标图像不同</span>
    <span class="kw">bool</span> blitMipLevel0 = image_preinitialized != image_final;
    <span class="kw">if</span> (generateMipmap || blitMipLevel0) {
        <span class="cmt">//满足条件的话录制命令</span>
        <span class="kw">auto</span>&amp; commandBuffer = <span class="type">graphicsBase</span>::<span class="fn">Plus</span>().<span class="fn">CommandBuffer_Transfer</span>();
        commandBuffer.<span class="fn">Begin</span>(<span class="enum">VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT</span>);

        <span class="cmt">/*待填充*/</span>

        commandBuffer.<span class="fn">End</span>();
        <span class="cmt">//执行命令</span>
        <span class="type">graphicsBase</span>::<span class="fn">Plus</span>().<span class="fn">ExecuteCommandBuffer_Graphics</span>(commandBuffer);
    }
}
</pre>
<ul>
    <li>
        <p>
            当然存在只生成mipmap的情况（比如为程序运行期间渲染的图像生成mipmap），尽管在本套教程中不会涉及此类使用场景。
        </p>
    </li>
</ul>
<p>
    不必多说，在blit前将源图像<span class="var">image_preinitialized</span>的内存布局从<span class="enum">VK_IMAGE_LAYOUT_PREINITIALIZED</span>转换到<span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span>：
</p>
<pre class="code">
<span class="kw">if</span> (generateMipmap || blitMipLevel0) {
    <span class="kw">auto</span>&amp; commandBuffer = <span class="type">graphicsBase</span>::<span class="fn">Plus</span>().<span class="fn">CommandBuffer_Transfer</span>();
    commandBuffer.<span class="fn">Begin</span>(<span class="enum">VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT</span>);

    <span class="cmt">//如有必要，从image_preinitialized将原图blit到image_final</span>
    <span class="kw">if</span> (blitMipLevel0) {
        <span class="enum">VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER</span>,
            <span class="kw">nullptr</span>,
            0,
            <span class="enum">VK_ACCESS_TRANSFER_READ_BIT</span>,
            <span class="enum">VK_IMAGE_LAYOUT_PREINITIALIZED</span>,
            <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span>,
            <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
            <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
            image_preinitialized,
            { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 1, 0, layerCount }
        };
        <span class="fn">vkCmdPipelineBarrier</span>(commandBuffer, <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>, <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, 0,
            0, <span class="kw">nullptr</span>, 0, <span class="kw">nullptr</span>, 1, &amp;imageMemoryBarrier);
        <span class="cmt">/*待填充*/</span>
    }

    <span class="cmt">//如有必要，生成mipmap</span>
    <span class="cmt">/*待填充*/</span>

    commandBuffer.<span class="fn">End</span>();
    <span class="type">graphicsBase</span>::<span class="fn">Plus</span>().<span class="fn">ExecuteCommandBuffer_Graphics</span>(commandBuffer);
}
</pre>
<p>
    填写<span class="type">VkImageBlit</span>并调用<span class="fn">CmdBlitImage</span>(...)：
</p>
<pre class="code">
<span class="kw">if</span> (generateMipmap || blitMipLevel0) {
    <span class="kw">auto</span>&amp; commandBuffer = <span class="type">graphicsBase</span>::<span class="fn">Plus</span>().<span class="fn">CommandBuffer_Transfer</span>();
    commandBuffer.<span class="fn">Begin</span>(<span class="enum">VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT</span>);

    <span class="cmt">//如有必要，从image_preinitialized将原图blit到image_final</span>
    <span class="kw">if</span> (blitMipLevel0) {
    <span class="type">VkImageMemoryBarrier</span> imageMemoryBarrier = {
        <span class="enum">VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER</span>,
            <span class="kw">nullptr</span>,
            0,
            <span class="enum">VK_ACCESS_TRANSFER_READ_BIT</span>,
            <span class="enum">VK_IMAGE_LAYOUT_PREINITIALIZED</span>,
            <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span>,
            <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
            <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
            image_preinitialized,
            { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 1, 0, layerCount }
        };
        <span class="fn">vkCmdPipelineBarrier</span>(commandBuffer, <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>, <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, 0,
            0, <span class="kw">nullptr</span>, 0, <span class="kw">nullptr</span>, 1, &amp;imageMemoryBarrier);
        <span class="type">VkImageBlit</span> region = {
            { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 0, layerCount },
            { {}, { <span class="type">int32_t</span>(imageExtent.width), <span class="type">int32_t</span>(imageExtent.height), 1 } },
            { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 0, layerCount },
            { {}, { <span class="type">int32_t</span>(imageExtent.width), <span class="type">int32_t</span>(imageExtent.height), 1 } }
        };
        <span class="kw">if</span> (generateMipmap)
            <span class="type">imageOperation</span>::<span class="fn">CmdBlitImage</span>(commandBuffer, image_preinitialized, image_final, region,
                { <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>, 0, <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span> },
                { <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, <span class="enum">VK_ACCESS_TRANSFER_READ_BIT</span>, <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span> }, minFilter);
        <span class="kw">else</span>
            <span class="type">imageOperation</span>::<span class="fn">CmdBlitImage</span>(commandBuffer, image_preinitialized, image_final, region,
                { <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>, 0, <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span> },
                { <span class="enum">VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT</span>, <span class="enum">VK_ACCESS_SHADER_READ_BIT</span>, <span class="enum">VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL</span> }, minFilter);
    }

    <span class="cmt">//如有必要，生成mipmap</span>
    <span class="cmt">/*待填充*/</span>

    commandBuffer.<span class="fn">End</span>();
    <span class="type">graphicsBase</span>::<span class="fn">Plus</span>().<span class="fn">ExecuteCommandBuffer_Graphics</span>(commandBuffer);
}
</pre>
<p>
    这里根据之后是否生成mipmap进行了分支：
    <br>
    如果之后生成mipmap，那么将内存布局转到<span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span>（剧透：生成mipmap也是通过blit命令达成的）；
    <br>
    否则转到<span class="enum">VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL</span>，以备后续在片段着色器（对应<span class="enum">VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT</span>）中被采样（属于<span class="enum">VK_ACCESS_SHADER_READ_BIT</span>）。
    <br>
    不过上述写法略显重复冗余，略做优化：
</p>
<pre class="code">
<span class="kw">static constexpr</span> <span class="type">imageOperation</span>::<span class="type">imageMemoryBarrierParameterPack</span> imbs[2] = {
    { <span class="enum">VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT</span>, <span class="enum">VK_ACCESS_SHADER_READ_BIT</span>, <span class="enum">VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL</span> },
    { <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, <span class="enum">VK_ACCESS_TRANSFER_READ_BIT</span>, <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span> }
};
<span class="kw">if</span> (generateMipmap || blitMipLevel0) {
    <span class="kw">auto</span>&amp; commandBuffer = <span class="type">graphicsBase</span>::<span class="fn">Plus</span>().<span class="fn">CommandBuffer_Transfer</span>();
    commandBuffer.<span class="fn">Begin</span>(<span class="enum">VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT</span>);

    <span class="cmt">//如有必要，从image_preinitialized将原图blit到image_final</span>
    <span class="kw">if</span> (blitMipLevel0) {
    <span class="type">VkImageMemoryBarrier</span> imageMemoryBarrier = {
        <span class="enum">VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER</span>,
            <span class="kw">nullptr</span>,
            0,
            <span class="enum">VK_ACCESS_TRANSFER_READ_BIT</span>,
            <span class="enum">VK_IMAGE_LAYOUT_PREINITIALIZED</span>,
            <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span>,
            <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
            <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
            image_preinitialized,
            { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 1, 0, layerCount }
        };
        <span class="fn">vkCmdPipelineBarrier</span>(commandBuffer, <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>, <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, 0,
            0, <span class="kw">nullptr</span>, 0, <span class="kw">nullptr</span>, 1, &amp;imageMemoryBarrier);
        <span class="type">VkImageBlit</span> region = {
            { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 0, layerCount },
            { {}, { <span class="type">int32_t</span>(imageExtent.width), <span class="type">int32_t</span>(imageExtent.height), 1 } },
            { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 0, layerCount },
            { {}, { <span class="type">int32_t</span>(imageExtent.width), <span class="type">int32_t</span>(imageExtent.height), 1 } }
        };
        <span class="type">imageOperation</span>::<span class="fn">CmdBlitImage</span>(commandBuffer, image_preinitialized, image_final, region,
            { <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>, 0, <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span> }, imbs[generateMipmap], minFilter); <span class="cmt">//因为没有放缩，滤波方式无所谓</span>
    }

    <span class="cmt">//如有必要，生成mipmap</span>
    <span class="cmt">/*待填充*/</span>

    commandBuffer.<span class="fn">End</span>();
    <span class="type">graphicsBase</span>::<span class="fn">Plus</span>().<span class="fn">ExecuteCommandBuffer_Graphics</span>(commandBuffer);
}
</pre></section>
<section id="copyblitandgeneratemipmap2d">
<h3>CopyBlitAndGenerateMipmap2d<a class="headerlink" href="#copyblitandgeneratemipmap2d" title="Permalink to this heading"></a></h3>
<p>
    <span class="fn">CopyBlitAndGenerateMipmap2d</span>(...)的逻辑与<span class="fn">BlitAndGenerateMipmap2d</span>(...)类似，只不过copy这一步一定发生。
    <br>
    填写<span class="type">VkBufferImageCopy</span>结构体，调用<span class="type">imageOperation</span>::<span class="fn">CmdCopyBufferToImage</span>(...)将图像从缓冲区<span class="var">buffer_copyFrom</span>拷贝到图像<span class="var">image_copyTo</span>：
</p>
<pre class="code">
<span class="kw">static void</span> <span class="fn">CopyBlitAndGenerateMipmap2d</span>(<span class="type">VkBuffer</span> buffer_copyFrom, <span class="type">VkImage</span> image_copyTo, <span class="type">VkImage</span> image_blitTo, <span class="type">VkExtent2D</span> imageExtent,
    <span class="type">uint32_t</span> mipLevelCount = 1, <span class="type">uint32_t</span> layerCount = 1, <span class="type">VkFilter</span> minFilter = <span class="enum">VK_FILTER_LINEAR</span>) {
    <span class="kw">static constexpr</span> <span class="type">imageOperation</span>::<span class="type">imageMemoryBarrierParameterPack</span> imbs[2] = {
        { <span class="enum">VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT</span>, <span class="enum">VK_ACCESS_SHADER_READ_BIT</span>, <span class="enum">VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL</span> },
        { <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, <span class="enum">VK_ACCESS_TRANSFER_READ_BIT</span>, <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span> }
    };
    <span class="cmt">//生成mipmap的条件是mip等级大于1</span>
    <span class="kw">bool</span> generateMipmap = mipLevelCount > 1;
    <span class="cmt">//发生成blit的条件是源图像和目标图像不同</span>
    <span class="kw">bool</span> blitMipLevel0 = image_copyTo != image_final;
    <span class="cmt">//录制命令</span>
    <span class="kw">auto</span>&amp; commandBuffer = <span class="type">graphicsBase</span>::<span class="fn">Plus</span>().<span class="fn">CommandBuffer_Transfer</span>();
    commandBuffer.<span class="fn">Begin</span>(<span class="enum">VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT</span>);

    <span class="type">VkBufferImageCopy</span> region = {
        .imageSubresource = { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 0, layerCount },
        .imageExtent = { imageExtent.width, imageExtent.height, 1 }
    };
    <span class="type">imageOperation</span>::<span class="fn">CmdCopyBufferToImage</span>(commandBuffer, buffer_copyFrom, image_copyTo, region,
        { <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>, 0, <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span> }, imbs[generateMipmap || blitMipLevel0]);

    <span class="cmt">/*待填充*/</span>

    commandBuffer.<span class="fn">End</span>();
    <span class="cmt">//执行命令</span>
    <span class="type">graphicsBase</span>::<span class="fn">Plus</span>().<span class="fn">ExecuteCommandBuffer_Graphics</span>(commandBuffer);
}
</pre>
<p>
    接着从<span class="var">image_copyTo</span>将数据blit到<span class="var">image_blitTo</span>，写法跟<span class="fn">BlitAndGenerateMipmap2d</span>(...)中一样：
</p>
<pre class="code">
<span class="type">VkBufferImageCopy</span> region = {
    .imageSubresource = { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 0, layerCount },
    .imageExtent = { imageExtent.width, imageExtent.height, 1 }
};
<span class="type">imageOperation</span>::<span class="fn">CmdCopyBufferToImage</span>(commandBuffer, buffer_copyFrom, image_copyTo, region,
    { <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>, 0, <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span> }, imbs[generateMipmap || blitMipLevel0]);

<span class="cmt">//如有必要，进行blit</span>
<span class="kw">if</span> (blitMipLevel0) {
    <span class="type">VkImageBlit</span> region = {
        { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 0, layerCount },
        { {}, { <span class="type">int32_t</span>(imageExtent.width), <span class="type">int32_t</span>(imageExtent.height), 1 } },
        { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 0, layerCount },
        { {}, { <span class="type">int32_t</span>(imageExtent.width), <span class="type">int32_t</span>(imageExtent.height), 1 } }
    };
    <span class="type">imageOperation</span>::<span class="fn">CmdBlitImage</span>(commandBuffer, image_preinitialized, image_final, region,
        { <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>, 0, <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span> }, imbs[generateMipmap], minFilter);
}

<span class="cmt">//如有必要，生成mipmap</span>
<span class="cmt">/*待填充*/</span>
</pre></section>
</section>
<section id="mipmap">
<h2>生成Mipmap<a class="headerlink" href="#mipmap" title="Permalink to this heading"></a></h2>
<p>
    Mipmap是一组原图像的的微缩版本，每个等级mipmap的长宽都是上一等级的一半，如下图所示：
</p>
<img alt="_images/ch5-2-1.png" src="_images/ch5-2-1.png" width="480px">
<p>
    <strong>Mipmap的存在意义是降低采样开销。</strong>
    <br>
    举例而言：将一张16x16大小的图像渲染到4x4大小，滤波方式为线性插值<span class="enum">VK_FILTER_LINEAR</span>，那么渲染出的一个像素就对应原图中4x4的区域，每个像素得采样原图的16个像素。
    <br>
    若没有mipmap且滤波方式不是<span class="enum">VK_FILTER_NEAREST</span>的话，无论原图被渲染到多小，都得采样整张原图，这在原图很大时显然很不划算。事先生成mipmap，再根据渲染时的放缩程度选择相应的mip等级进行采样，就能大幅降低开销。
</p><section id="mip">
<h3>计算Mip等级数<a class="headerlink" href="#mip" title="Permalink to this heading"></a></h3>
<p>
    创建图像时填写的<span clss="enum">VkImageCreateInfo</span>::mipLevels指的是微缩图的总数，计入缩放比为1的原尺寸图像，因此最小为1。
</p>
<p>
    <strong>计算各个mip等级的图像的大小：</strong>
    <br>
    原图的mip等级为0，从mip等级1开始的每一个mip等级的图像，尺寸都是上一等级的一半。若上一等级的长或宽是奇数，则取不足近似值；若上一等级的长或宽已经是1了，则取1（<a href="https://renderdoc.org/vkspec_chunked/chap12.html#resources-image-mip-level-sizing">官方标准中的说明见此</a>），即：
    <br>
    <code>extent[i + 1].width = <span class="fn">max</span>(extent[i].width / 2, 1);</code>
    <br>
    等价于下式：
    <br>
    <code>extent[i].width = <span class="fn">max</span>(extent[0].width >> i, 1);</code>
</p>
<p>
    <strong>计算mip等级总数：</strong>
    <br>
    一套完整的mipmap是从原图到1x1大小的一系列图像，这意味着mip等级总数的计算步骤为：取长宽中较大者，取2的对数，再对结果取不足近似值并加1。
    <br>
    在<span class="type">texture</span>中定义以下函数以计算mip等级总数：
</p>
<pre class="code">
<span class="kw">static</span> <span class="type">uint32_t</span> <span class="fn">CalculateMipLevelCount</span>(<span class="type">VkExtent2D</span> extent) {
    <span class="kw">return</span> <span class="type">uint32_t</span>(std::<span class="fn">floor</span>(std::<span class="fn">log2</span>(std::<span class="fn">max</span>(extent.width, extent.height)))) + 1;
}
</pre></section>
<section id="cmdgeneratemipmap2d">
<h3>CmdGenerateMipmap2d<a class="headerlink" href="#cmdgeneratemipmap2d" title="Permalink to this heading"></a></h3>
<p>
    先前在<a class="reference internal" href="Ch7-5%20%E6%8B%B7%E8%B4%9D%E5%9B%BE%E5%83%8F%E5%88%B0%E5%B1%8F%E5%B9%95.html">Ch7-5 拷贝图像到屏幕</a>定义了<span class="type">imageOperation</span>，现在其中加入静态成员函数<span class="fn">CmdGenerateMipmap2d</span>(...)：
</p>
<pre class="code">
<span class="kw">static void</span> <span class="fn">CmdGenerateMipmap2d</span>(<span class="type">VkCommandBuffer</span> commandBuffer, <span class="type">VkImage</span> image, <span class="type">VkExtent2D</span> imageExtent, <span class="type">uint32_t</span> mipLevelCount, <span class="type">uint32_t</span> layerCount,
    <span class="type">imageMemoryBarrierParameterPack</span> imb_to, <span class="type">VkFilter</span> minFilter = <span class="enum">VK_FILTER_LINEAR</span>) {
    <span class="cmt">/*待填充*/</span>
}
</pre>
<p>
    定义一个lambda表达式来计算mipmap的大小，因为一会儿要填写<span class="type">VkImageBlit</span>结构体，这里直接返回一个<span class="type">VkOffset3D</span>：
</p>
<pre class="code">
<span class="kw">auto</span> MipmapExtent = [](<span class="type">VkExtent2D</span> imageExtent, <span class="type">uint32_t</span> mipLevel) {
    <span class="type">VkOffset3D</span> extent = { std::<span class="fn">max</span>(<span class="type">int32_t</span>(imageExtent.width &gt;&gt; mipLevel), 1), std::<span class="fn">max</span>(<span class="type">int32_t</span>(imageExtent.height &gt;&gt; mipLevel), 1), 1 };
    <span class="kw">return</span> extent;
};
</pre>
<p>
    这里std::<span class="fn">max</span>(...)的作用是把0变成1，既是如此就可以将其优化掉：
</p>
<pre class="code">
<span class="kw">auto</span> MipmapExtent = [](<span class="type">VkExtent2D</span> imageExtent, <span class="type">uint32_t</span> mipLevel) {
    <span class="type">VkOffset3D</span> extent = { <span class="type">int32_t</span>(imageExtent.width &gt;&gt; mipLevel), <span class="type">int32_t</span>(imageExtent.height &gt;&gt; mipLevel), 1 };
    extent.x += !extent.x;
    extent.y += !extent.y;
    <span class="kw">return</span> extent;
};
</pre>
<p>
    既然mipmap是原图的微缩版本，那么当然是用blit命令来生成的啦，先在循环中填写<span class="type">VkImageBlit</span>结构体：
</p>
<pre class="code">
<span class="kw">for</span> (<span class="type">uint32_t</span> i = 1; i &lt; mipLevelCount; i++) {
    <span class="type">VkImageBlit</span> region = {
        { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, i - 1, 0, layerCount },<span class="cmt">//srcSubresource</span>
        { {}, MipmapExtent(imageExtent, i - 1) },           <span class="cmt">//srcOffsets</span>
        { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, i, 0, layerCount },    <span class="cmt">//dstSubresource</span>
        { {}, MipmapExtent(imageExtent, i) }                <span class="cmt">//dstOffsets</span>
    };
    <span class="fn">CmdBlitImage</span>(<span class="cmt">/*待填充*/</span>);
}
</pre>
<p>
    接着来考虑<span class="fn">CmdBlitImage</span>(...)的参数：
    <br>
    一套mipmap属于同一图像底下的资源，因此源图像和目标图像相同。
    <br>
    每个mip级别在被blit入数据前没有数据，内存布局填写<span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span>即可，blit后转到<span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span>，以备blit到下一mip级别。
    <br>
    每个blit命令既要等待先前的blit结束（这样才能读取上一mip级别），又要在下一次blit前完成（除了最后一个mip级别），所以前后管线屏障中填写的阶段都是<span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>。
</p>
<pre class="code">
<span class="kw">for</span> (<span class="type">uint32_t</span> i = 1; i &lt; mipLevelCount; i++) {
    <span class="type">VkImageBlit</span> region = {
        { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, i - 1, 0, layerCount },<span class="cmt">//srcSubresource</span>
        { {}, MipmapExtent(imageExtent, i - 1) },           <span class="cmt">//srcOffsets</span>
        { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, i, 0, layerCount },    <span class="cmt">//dstSubresource</span>
        { {}, MipmapExtent(imageExtent, i) }                <span class="cmt">//dstOffsets</span>
    };
    <span class="fn">CmdBlitImage</span>(</span>commandBuffer, image, image, region,
        { <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, 0, <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span> },
        { <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, <span class="enum">VK_ACCESS_TRANSFER_READ_BIT</span>, <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span> }, minFilter);
}
</pre>
<p>
    这么一来，所有mip级别的内存布局都转到了<span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span>，包括没必要转过去的1x1大小的mip级别。
    <br>
    接着用一个内存屏障将它们一次性全部转到参数<span class="var">imb_to</span>所指定的内存布局：
</p>
<pre class="code">
<span class="kw">if</span> (imb_to.isNeeded) {
    <span class="type">VkImageMemoryBarrier</span> imageMemoryBarrier = {
        <span class="enum">VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER</span>,
        <span class="kw">nullptr</span>,
        0,
        imb_to.access,
        <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span>,
        imb_to.layout,
        <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
        <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
        image,
        { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, mipLevelCount, 0, layerCount }
    };
    <span class="fn">vkCmdPipelineBarrier</span>(commandBuffer, <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, imb_to.stage, 0,
        0, <span class="kw">nullptr</span>, 0, <span class="kw">nullptr</span>, 1, &amp;imageMemoryBarrier);
}
</pre>
<p>
    1x1大小的那张在blit完成后还没有被使用过，然后按照<span class="enum">VK_ACCESS_TRANSFER_READ_BIT</span>→<span class="var">imb_to</span>.layout的顺序，经历两次内存布局转换（<a class="reference internal" href="Ch3-1%20%E5%90%8C%E6%AD%A5%E5%8E%9F%E8%AF%AD.html#id13">内存布局转换</a>具有隐式同步，不会被重排）。
</p>
<p>
    整个函数如下：
</p>
<pre class="code">
<span class="kw">static void</span> <span class="fn">CmdGenerateMipmap2d</span>(<span class="type">VkCommandBuffer</span> commandBuffer, <span class="type">VkImage</span> image, <span class="type">VkExtent2D</span> imageExtent, <span class="type">uint32_t</span> mipLevelCount, <span class="type">uint32_t</span> layerCount,
    <span class="type">imageMemoryBarrierParameterPack</span> imb_to, <span class="type">VkFilter</span> minFilter = <span class="enum">VK_FILTER_LINEAR</span>) {
    <span class="kw">auto</span> MipmapExtent = [](<span class="type">VkExtent2D</span> imageExtent, <span class="type">uint32_t</span> mipLevel) {
        <span class="type">VkOffset3D</span> extent = { <span class="type">int32_t</span>(imageExtent.width &gt;&gt; mipLevel), <span class="type">int32_t</span>(imageExtent.height &gt;&gt; mipLevel), 1 };
        extent.x += !extent.x;
        extent.y += !extent.y;
        <span class="kw">return</span> extent;
    };
    <span class="kw">for</span> (<span class="type">uint32_t</span> i = 1; i &lt; mipLevelCount; i++) {
        <span class="type">VkImageBlit</span> region = {
            { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, i - 1, 0, layerCount },<span class="cmt">//srcSubresource</span>
            { {}, MipmapExtent(imageExtent, i - 1) },           <span class="cmt">//srcOffsets</span>
            { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, i, 0, layerCount },    <span class="cmt">//dstSubresource</span>
            { {}, MipmapExtent(imageExtent, i) }                <span class="cmt">//dstOffsets</span>
        };
        <span class="fn">CmdBlitImage</span>(</span>commandBuffer, image, image, region,
            { <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, 0, <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span> },
            { <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, <span class="enum">VK_ACCESS_TRANSFER_READ_BIT</span>, <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span> }, minFilter);
    }
    <span class="kw">if</span> (imb_to.isNeeded) {
        <span class="type">VkImageMemoryBarrier</span> imageMemoryBarrier = {
            <span class="enum">VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER</span>,
            <span class="kw">nullptr</span>,
            0,
            imb_to.access,
            <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span>,
            imb_to.layout,
            <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
            <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
            image,
            { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, mipLevelCount, 0, layerCount }
        };
        <span class="fn">vkCmdPipelineBarrier</span>(commandBuffer, <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, imb_to.stage, 0,
            0, <span class="kw">nullptr</span>, 0, <span class="kw">nullptr</span>, 1, &amp;imageMemoryBarrier);
    }
}
</pre>
<p>
    。。。你以为事情这么一来就结束了吗？天真！
    <br>
    这个函数被设计为也适用于给贴图数组生成mipmap，然而，在一些GPU上（比如我的Nvidia 1060 Max-Q），为NPOT（Non-power-of-two，长宽非2的幂）图像的数组生成mipmap时，会出现如下奇异现象：
</p>
<img alt="_images/ch5-2-2.png" src="_images/ch5-2-2.png" width="1048px">
<p>
    上图渲染了LastResortHE-Regular字体中的所有字形，左图是期望的情况，右图中则出现了诡异的重复。
</p>
<p>
    我没有在英伟达的文档里查到具体原因，也不清楚其他GPU是否可能出问题。
    <br>
    为防止这种现象的发生，出于保险起见，可以对先前代码中的循环部分进行修改。因完全基于先前所学内容，解说略：
</p>
<pre class="code">
<span class="kw">static void</span> <span class="fn">CmdGenerateMipmap2d</span>(<span class="type">VkCommandBuffer</span> commandBuffer, <span class="type">VkImage</span> image, <span class="type">VkExtent2D</span> imageExtent, <span class="type">uint32_t</span> mipLevelCount, <span class="type">uint32_t</span> layerCount,
    <span class="type">imageMemoryBarrierParameterPack</span> imb_to, <span class="type">VkFilter</span> minFilter = <span class="enum">VK_FILTER_LINEAR</span>) {
    <span class="kw">auto</span> MipmapExtent = [](<span class="type">VkExtent2D</span> imageExtent, <span class="type">uint32_t</span> mipLevel) {
        <span class="type">VkOffset3D</span> extent = { <span class="type">int32_t</span>(imageExtent.width &gt;&gt; mipLevel), <span class="type">int32_t</span>(imageExtent.height &gt;&gt; mipLevel), 1 };
        extent.x += !extent.x;
        extent.y += !extent.y;
        <span class="kw">return</span> extent;
    };
    <span class="kw">if</span> (layerCount > 1) {
        std::<span class="type">unique_ptr</span>&lt;<span class="type">VkImageBlit</span>[]&gt; regions = std::<span class="fn">make_unique</span>&lt;<span class="type">VkImageBlit</span>[]&gt;(layerCount);
        <span class="kw">for</span> (<span class="type">uint32_t</span> i = 1; i &lt; mipLevelCount; i++) {
            <span class="type">VkOffset3D</span> mipmapExtent_src = MipmapExtent(imageExtent, i - 1);
            <span class="type">VkOffset3D</span> mipmapExtent_dst = MipmapExtent(imageExtent, i);
            <span class="kw">for</span> (<span class="type">uint32_t</span> j = 1; j &lt; layerCount; j++) {
                regions[j] = {
                    { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, i - 1, j, 1 },
                    { {}, mipmapExtent_src },
                    { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, i, j, 1 },
                    { {}, mipmapExtent_dst }
                };
            <span class="type">VkImageMemoryBarrier</span> imageMemoryBarrier = {
                <span class="enum">VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER</span>,
                <span class="kw">nullptr</span>,
                0,
                <span class="enum">VK_ACCESS_TRANSFER_WRITE_BIT</span>,
                <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span>,
                <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL</span>,
                <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
                <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
                image,
                { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, i, 1, 0, layerCount }
            };
            <span class="fn">vkCmdPipelineBarrier</span>(commandBuffer, <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, 0,
                0, <span class="kw">nullptr</span>, 0, <span class="kw">nullptr</span>, 1, &amp;imageMemoryBarrier);
            <span class="fn">vkCmdBlitImage</span>(commandBuffer,
                image, <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span>,
                image, <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL</span>,
                layerCount, regions.<span class="fn">get</span>(), minFilter);
            imageMemoryBarrier.srcAccessMask = <span class="enum">VK_ACCESS_TRANSFER_WRITE_BIT</span>;
            imageMemoryBarrier.oldLayout = <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL</span>;
            imageMemoryBarrier.dstAccessMask = <span class="enum">VK_ACCESS_TRANSFER_READ_BIT</span>;
            imageMemoryBarrier.newLayout = <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span>;
            <span class="fn">vkCmdPipelineBarrier</span>(commandBuffer, <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, 0,
                0, <span class="kw">nullptr</span>, 0, <span class="kw">nullptr</span>, 1, &amp;imageMemoryBarrier);
        }
    }
    <span class="kw">else</span>
        <span class="kw">for</span> (<span class="type">uint32_t</span> i = 1; i &lt; mipLevelCount; i++) {
            <span class="type">VkImageBlit</span> region = {
                { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, i - 1, 0, 1 },
                { {}, MipmapExtent(imageExtent, i - 1) },
                { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, i, 0, 1 },
                { {}, MipmapExtent(imageExtent, i) }
            };
            <span class="fn">CmdBlitImage</span>(</span>commandBuffer, image, image, region,
                { <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, 0, <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span> },
                { <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, <span class="enum">VK_ACCESS_TRANSFER_READ_BIT</span>, <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span> }, minFilter);
        }
    <span class="kw">if</span> (imb_to.isNeeded) {
        <span class="type">VkImageMemoryBarrier</span> imageMemoryBarrier = {
            <span class="enum">VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER</span>,
            <span class="kw">nullptr</span>,
            0,
            imb_to.access,
            <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span>,
            imb_to.layout,
            <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
            <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
            image,
            { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, mipLevelCount, 0, layerCount }
        };
        <span class="fn">vkCmdPipelineBarrier</span>(commandBuffer, <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, imb_to.stage, 0,
            0, <span class="kw">nullptr</span>, 0, <span class="kw">nullptr</span>, 1, &amp;imageMemoryBarrier);
    }
}
</pre>
<p>
    现在来完成先前的<span class="fn">CopyBlitAndGenerateMipmap2d</span>(...)和<span class="fn">BlitAndGenerateMipmap2d</span>(...)两个函数：
</p>
<pre class="code">
<span class="kw">static void</span> <span class="fn">BlitAndGenerateMipmap2d</span>(<span class="type">VkImage</span> image_preinitialized, <span class="type">VkImage</span> image_final, <span class="type">VkExtent2D</span> imageExtent,
    <span class="type">uint32_t</span> mipLevelCount = 1, <span class="type">uint32_t</span> layerCount = 1, <span class="type">VkFilter</span> minFilter = <span class="enum">VK_FILTER_LINEAR</span>) {
    <span class="kw">static constexpr</span> <span class="type">imageOperation</span>::<span class="type">imageMemoryBarrierParameterPack</span> imbs[2] = {
        { <span class="enum">VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT</span>, <span class="enum">VK_ACCESS_SHADER_READ_BIT</span>, <span class="enum">VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL</span> },
        { <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, <span class="enum">VK_ACCESS_TRANSFER_READ_BIT</span>, <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span> }
    };
    <span class="kw">bool</span> generateMipmap = mipLevelCount > 1;
    <span class="kw">bool</span> blitMipLevel0 = image_preinitialized != image_final;
    <span class="kw">if</span> (generateMipmap || blitMipLevel0) {
        <span class="kw">auto</span>&amp; commandBuffer = <span class="type">graphicsBase</span>::<span class="fn">Plus</span>().<span class="fn">CommandBuffer_Transfer</span>();
        commandBuffer.<span class="fn">Begin</span>(<span class="enum">VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT</span>);

        <span class="kw">if</span> (blitMipLevel0) {
        <span class="type">VkImageMemoryBarrier</span> imageMemoryBarrier = {
            <span class="enum">VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER</span>,
                <span class="kw">nullptr</span>,
                0,
                <span class="enum">VK_ACCESS_TRANSFER_READ_BIT</span>,
                <span class="enum">VK_IMAGE_LAYOUT_PREINITIALIZED</span>,
                <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span>,
                <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
                <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
                image_preinitialized,
                { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 1, 0, layerCount }
            };
            <span class="fn">vkCmdPipelineBarrier</span>(commandBuffer, <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>, <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, 0,
                0, <span class="kw">nullptr</span>, 0, <span class="kw">nullptr</span>, 1, &amp;imageMemoryBarrier);
            <span class="type">VkImageBlit</span> region = {
                { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 0, layerCount },
                { {}, { <span class="type">int32_t</span>(imageExtent.width), <span class="type">int32_t</span>(imageExtent.height), 1 } },
                { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 0, layerCount },
                { {}, { <span class="type">int32_t</span>(imageExtent.width), <span class="type">int32_t</span>(imageExtent.height), 1 } }
            };
            <span class="type">imageOperation</span>::<span class="fn">CmdBlitImage</span>(commandBuffer, image_preinitialized, image_final, region,
                { <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>, 0, <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span> }, imbs[generateMipmap], minFilter);
        }

        <span class="kw">if</span> (generateMipmap)
            <span class="type">imageOperation</span>::<span class="fn">CmdGenerateMipmap2d</span>(commandBuffer, image_final, imageExtent, mipLevelCount, layerCount,
                { <span class="enum">VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT</span>, <span class="enum">VK_ACCESS_SHADER_READ_BIT</span>, <span class="enum">VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL</span> }, minFilter);

        commandBuffer.<span class="fn">End</span>();
        <span class="type">graphicsBase</span>::<span class="fn">Plus</span>().<span class="fn">ExecuteCommandBuffer_Graphics</span>(commandBuffer);
    }
}
<span class="kw">static void</span> <span class="fn">CopyBlitAndGenerateMipmap2d</span>(<span class="type">VkBuffer</span> buffer_copyFrom, <span class="type">VkImage</span> image_copyTo, <span class="type">VkImage</span> image_blitTo, <span class="type">VkExtent2D</span> imageExtent,
    <span class="type">uint32_t</span> mipLevelCount = 1, <span class="type">uint32_t</span> layerCount = 1, <span class="type">VkFilter</span> minFilter = <span class="enum">VK_FILTER_LINEAR</span>) {
    <span class="kw">static constexpr</span> <span class="type">imageOperation</span>::<span class="type">imageMemoryBarrierParameterPack</span> imbs[2] = {
        { <span class="enum">VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT</span>, <span class="enum">VK_ACCESS_SHADER_READ_BIT</span>, <span class="enum">VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL</span> },
        { <span class="enum">VK_PIPELINE_STAGE_TRANSFER_BIT</span>, <span class="enum">VK_ACCESS_TRANSFER_READ_BIT</span>, <span class="enum">VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL</span> }
    };
    <span class="kw">bool</span> generateMipmap = mipLevelCount > 1;
    <span class="kw">bool</span> blitMipLevel0 = image_copyTo != image_final;
    <span class="kw">auto</span>&amp; commandBuffer = <span class="type">graphicsBase</span>::<span class="fn">Plus</span>().<span class="fn">CommandBuffer_Transfer</span>();
    commandBuffer.<span class="fn">Begin</span>(<span class="enum">VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT</span>);

    <span class="type">VkBufferImageCopy</span> region = {
        .imageSubresource = { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 0, layerCount },
        .imageExtent = { imageExtent.width, imageExtent.height, 1 }
    };
    <span class="type">imageOperation</span>::<span class="fn">CmdCopyBufferToImage</span>(commandBuffer, buffer_copyFrom, image_copyTo, region,
        { <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>, 0, <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span> }, imbs[generateMipmap || blitMipLevel0]);

    <span class="kw">if</span> (blitMipLevel0) {
        <span class="type">VkImageBlit</span> region = {
            { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 0, layerCount },
            { {}, { <span class="type">int32_t</span>(imageExtent.width), <span class="type">int32_t</span>(imageExtent.height), 1 } },
            { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 0, layerCount },
            { {}, { <span class="type">int32_t</span>(imageExtent.width), <span class="type">int32_t</span>(imageExtent.height), 1 } }
        };
        <span class="type">imageOperation</span>::<span class="fn">CmdBlitImage</span>(commandBuffer, image_preinitialized, image_final, region,
            { <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>, 0, <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span> }, imbs[generateMipmap], minFilter);
    }

    <span class="kw">if</span> (generateMipmap)
        <span class="type">imageOperation</span>::<span class="fn">CmdGenerateMipmap2d</span>(commandBuffer, image_final, imageExtent, mipLevelCount, layerCount,
            { <span class="enum">VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT</span>, <span class="enum">VK_ACCESS_SHADER_READ_BIT</span>, <span class="enum">VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL</span> }, minFilter);

    commandBuffer.<span class="fn">End</span>();
    <span class="type">graphicsBase</span>::<span class="fn">Plus</span>().<span class="fn">ExecuteCommandBuffer_Graphics</span>(commandBuffer);
}
</pre>
<p>
    全部代码请比照<a href="https://github.com/EasyVulkan/EasyVulkan.github.io/tree/main/solution/EasyVulkan_Ch7/VkBase+.h">VkBase+.h</a>。
    <br>
    接着就是回到<a class="reference internal" href="Ch7-6%20%E4%BD%BF%E7%94%A8%E8%B4%B4%E5%9B%BE.html">Ch7-6 使用贴图</a>采样贴图了！
</p></section>
</section>
</section>


           </div>
          </div>
          <footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
        <a href="Ch5-1%20%E5%90%84%E7%A7%8D%E7%BC%93%E5%86%B2%E5%8C%BA.html" class="btn btn-neutral float-left" title="Ch5-1 各种缓冲区" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> 上一页</a>
        <a href="Ch7-1%20%E5%88%9D%E8%AF%86%E9%A1%B6%E7%82%B9%E7%BC%93%E5%86%B2%E5%8C%BA.html" class="btn btn-neutral float-right" title="Ch7-1 初识顶点缓冲区" accesskey="n" rel="next">下一页 <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
    </div>

  <hr/>

  <div role="contentinfo">
    <p>&#169; 版权所有 2021, Qiao YeCheng.</p>
  </div>

  利用 <a href="https://www.sphinx-doc.org/">Sphinx</a> 构建，使用了 
    <a href="https://github.com/readthedocs/sphinx_rtd_theme">主题</a>
    由 <a href="https://readthedocs.org">Read the Docs</a>开发.
   

</footer>
        </div>
      </div>
    </section>
  </div>
  <script>
      jQuery(function () {
          SphinxRtdTheme.Navigation.enable(true);
      });
  </script> 

</body>
</html>